﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Text.RegularExpressions;//Для регулярных выражений

namespace ConsoleLetsDoIt
{
    class Parser
    {
        /// <summary>
        /// метод возвращает в массив строк из двух элементов, один из которых - обычный комментарий, а другой - XML
        /// </summary>
        /// <param name="codeinput">Массив строк кода</param>
        /// <returns>Массив с комментарииями [0] - //,[1] - ///</returns>
        private string[] NormalPlusXMLcomments(string[] codeinput)
        {
            List<string> code = new List<string>(); //Массив кода очищаем от пустых строк
            for (int i = 0; i < codeinput.Length; i++) if (codeinput[i] != String.Empty) code.Add(codeinput[i]);

            ArrayList specialcomments = new ArrayList(); //Все блоки специальных комментариев
            string check = ""; //Переменная, в которую мы заносим 1 блок комментариев ///
            string result = ""; //А сюда мы заносим комментарии вида //
            string specialresult = ""; //Специальные комментарии
            string[] returncomments = new String[2]; //Массив строк 0 - обычные, 1 - XML
            for (int i = 0; i < code.Count; i++)
            {
                string input = ClearStringFromString(code[i]);
                string pattern = "[\\/]{2}.{1,}";//Наша регулярочка для комментариев
                Regex newReg = new Regex(pattern);//Шаманим с регулярками по средствам C#
                MatchCollection matches = newReg.Matches(input); //То, что нашаманили. Вызываем метод, куда отправляем строку для нахождения
                foreach (Match mat in matches) //Вытягиваем по 1 соовествию из коллекции строкой выше
                {
                    // Фильтруем комментарии для ///
                    if ((mat.Value.Remove(0, 2))[0] == '/') //Если у нас все же комментарий с /// в принципе если пользователь не нашаманил с ними, то должны выводиться только XML
                    {
                        check += mat.Value.Remove(0, 3) + "\r\n"; //Если тут комментарий вида ///, то есть добавляем в переменную и ставим перенос строки
                        if (i == code.Count - 1)
                        {
                            specialcomments.Add(check); //В массив
                            check = "";
                        }


                        else if (((((code[i + 1]).TrimStart())[0] != '/') && ((((code[i + 1]).TrimStart())[1] != '/') && (((code[i + 1]).TrimStart())[2] != '/'))) || ((((code[i + 1]).TrimStart())[0] == '/') && ((((code[i + 1]).TrimStart())[1] == '/') && (((code[i + 1]).TrimStart())[2] != '/')))) //Условие!!!
                        {

                            specialcomments.Add(check); //В массив
                            check = "";
                        }

                    }
                    else
                    {
                        result += "Комментарий в " + (i + 1).ToString() + " cтроке — " + mat.Value.Remove(0, 2) + "\r\n"; //Если у нас комментарий не вида ///, то просто красиво оформляем
                    }
                }
            }
            for (int g = 0; g < specialcomments.Count; g++)//Мы получили тут комменты которые вида /// разбитые на блоки их оформляем;
            {
                specialresult += "Блок специальных комментариев " + (g + 1).ToString() + "\r\n\r\n" + specialcomments[g].ToString() + "\r\nКонец блока " + (g + 1).ToString() + "\r\n";
            }

            if (result == "") returncomments[0] = "Нет обычных комментариев"; else returncomments[0] = result;
            if (specialresult == "") returncomments[1] = "Нет XML комментариев"; else returncomments[1] = specialresult;
            return returncomments;

        }
        /// <summary>
        /// Ищем многострочные комментарии 
        /// </summary>
        /// <param name="code">В тексте программы</param>
        /// <returns>string с кногостроными комментариямит</returns>
        private string MultilineComments(string code)//Метод для комментариев вида многострочные
        {
            string result = "";//Что мы вернем
            /* Поиск таких комментариев */
            string pattern2 = "(\\/\\*)[\\s\\S]{1,}?(\\*\\/)";//Супер регулярочка
            Regex newReg2 = new Regex(pattern2);//Шаманим
            MatchCollection matches2 = newReg2.Matches(code);//Оперируем в нашаманиным

            foreach (Match mat1 in matches2)//Вот тут уже весь текст обрабатываем так что foreach нам к лицу
            {
                string result2 = mat1.Value.Remove(0, 2); //удаляем "/ *"
                result2 = result2.Remove((result2.Length - 2), 2); //удаляем "* /"
                int counter = strcount("\r\n", mat1.Value);//Сколько строчек
                result += "Комментарий из " + (counter + 1).ToString() + " cтрок:\r\n" + result2 + "\r\n";
            }
            if (result == "") result = "Нет многострочных комментариев";
            return result;
        }
        /// <summary>
        /// Метод подсчета подстрок в строке
        /// </summary>
        /// <param name="search">Какую подстроку ищем</param>
        /// <param name="input">В какой строке</param>
        /// <returns>int Количество</returns>
        private int strcount(string search, string input)
        {
            int i = 0; //Числовая переменная, контролирующая итерации цикла
            int x = -1; //Так как метод IndexOf() возвращает "-1" если первое вхождение подстроки не найдено, то приходится использовать вспомагательную, вместо і, что б начать цикл 
            int count = -1; //Записываем количество вхождений (итераций цикла)
            while (i != -1)
            {
                i = input.IndexOf(search, x + 1); //получаем индекс первого вхождения х+1 говорит, что начинать нужно с 0-го индекса
                x = i; //соответственно присваиваем номер индекса первого значения, чтобы потом (x+1) начать со следующего
                count++; //Увеличиваем на единицу наше количество 
            }
            return count; //возвращаем это количество
        }
        /// <summary>
        /// Метод для отлова строчек вида string = "something";
        /// </summary>
        /// <param name="inputs">Вхождная строка данного вида</param>
        /// <returns>string хорошее</returns>
        private string ClearStringFromString(string inputs)
        {
            //Чистим строку от всяких элементов типа спецсимволов
            if (inputs.IndexOf(('\\').ToString() + "\"") != -1)
                inputs = inputs.Replace(('\\').ToString() + "\"", "x");
            if (inputs.IndexOf("'\"'") != -1)
                inputs = inputs.Replace("'\"'", "x");
            if (inputs.IndexOf("'\\\\\'") != -1)
                inputs = inputs.Replace("'\\\\\'", "x");

            char[] notgoodstring = inputs.ToCharArray();
            bool checkstring = false;

            for (int ij = 0; ij < notgoodstring.Length; ij++)
            {
                if (notgoodstring[ij] == '"')//если совпадение
                {
                    checkstring = !checkstring;

                }
                else if (checkstring == true)//если значение true
                {
                    notgoodstring[ij] = 'x';//присваиваем
                }
            }
            return new String(notgoodstring);//возвращаем значение
            
        }

        public delegate void ParserEventHandler(object sender, ParserEventArgs e); //делегат, к которому прекрепляются обработчики событий парсера
        public event ParserEventHandler WorkIsDone; //событие, которое происходит при завершении работы парсера

        
        /// <summary>
        /// Главный метод парсера, ищет все виды комментариев
        /// </summary>
        /// <param name="codeinput">Массив строк, нужен для нахождение обычных и XML комментариев</param>
        /// <param name="codeinput2">Строка, нужна для нахождения многострочных комментариев</param>
        public void Analyse(string[] codeinput, string codeinput2)
        {
            WorkIsDone(this, new ParserEventArgs(this.NormalPlusXMLcomments(codeinput),this.MultilineComments(codeinput2))); //оповещаем об окончании работы через событие
        } 

    }
}
